#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Xbox Controller Real-time Input Reader
实时读取Xbox手柄的所有按键和摇杆输入

Author: Yinchuan Li
Date: 2025-07-27
Description: 实时监控Xbox手柄的所有按键、摇杆和触发器状态
"""

import pygame
import time
import sys
from typing import Dict, List, Tuple

class XboxControllerReader:
    """Xbox手柄读取器类"""
    
    def __init__(self):
        """初始化Xbox手柄读取器"""
        # 初始化pygame
        pygame.init()
        pygame.joystick.init()
        
        # 检查是否有手柄连接
        if pygame.joystick.get_count() == 0:
            print("错误: 未检测到Xbox手柄，请确保手柄已连接")
            sys.exit(1)
        
        # 获取第一个手柄
        self.joystick = pygame.joystick.Joystick(0)
        self.joystick.init()
        
        print(f"成功连接手柄: {self.joystick.get_name()}")
        print(f"手柄轴数量: {self.joystick.get_numaxes()}")
        print(f"手柄按钮数量: {self.joystick.get_numbuttons()}")
        print(f"手柄帽子数量: {self.joystick.get_numhats()}")
        
        # Xbox按键映射表
        self.button_mapping = {
            0: "A (确认)",
            1: "B (取消)", 
            2: "X (功能)",
            3: "Y (功能)",
            4: "LB (左肩键)",
            5: "RB (右肩键)",
            6: "Back (选择键)",
            7: "Start (开始键)",
            8: "Xbox Logo",
            9: "Left Stick Click",
            10: "Right Stick Click",
            11: "Menu (菜单)"
        }
        
        # 摇杆轴映射
        self.axis_mapping = {
            0: "Left Stick X",
            1: "Left Stick Y", 
            2: "LT Trigger",
            3: "Right Stick X",
            4: "Right Stick Y",
            5: "RT Trigger"
        }
        
        # 帽子开关映射
        self.hat_mapping = {
            (0, 1): "D-Pad Up",
            (0, -1): "D-Pad Down",
            (-1, 0): "D-Pad Left", 
            (1, 0): "D-Pad Right",
            (-1, 1): "D-Pad Up-Left",
            (1, 1): "D-Pad Up-Right",
            (-1, -1): "D-Pad Down-Left",
            (1, -1): "D-Pad Down-Right"
        }
        
        # 状态存储
        self.button_states = {}
        self.axis_states = {}
        self.hat_states = {}
        
        # 初始化状态
        self._init_states()
        
    def _init_states(self):
        """初始化所有状态"""
        # 初始化按钮状态
        for i in range(self.joystick.get_numbuttons()):
            self.button_states[i] = False
            
        # 初始化轴状态
        for i in range(self.joystick.get_numaxes()):
            self.axis_states[i] = 0.0
            
        # 初始化帽子状态
        for i in range(self.joystick.get_numhats()):
            self.hat_states[i] = (0, 0)
    
    def read_inputs(self) -> Dict:
        """读取所有输入状态"""
        # 处理pygame事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return None
        
        # 读取按钮状态
        button_data = {}
        for i in range(self.joystick.get_numbuttons()):
            button_pressed = self.joystick.get_button(i)
            button_data[i] = {
                'name': self.button_mapping.get(i, f"Button {i}"),
                'pressed': button_pressed,
                'changed': button_pressed != self.button_states.get(i, False)
            }
            self.button_states[i] = button_pressed
        
        # 读取轴状态
        axis_data = {}
        for i in range(self.joystick.get_numaxes()):
            axis_value = self.joystick.get_axis(i)
            axis_data[i] = {
                'name': self.axis_mapping.get(i, f"Axis {i}"),
                'value': axis_value,
                'changed': abs(axis_value - self.axis_states.get(i, 0)) > 0.01
            }
            self.axis_states[i] = axis_value
        
        # 读取帽子状态
        hat_data = {}
        for i in range(self.joystick.get_numhats()):
            hat_value = self.joystick.get_hat(i)
            hat_data[i] = {
                'name': f"Hat {i}",
                'value': hat_value,
                'direction': self.hat_mapping.get(hat_value, "Center"),
                'changed': hat_value != self.hat_states.get(i, (0, 0))
            }
            self.hat_states[i] = hat_value
        
        return {
            'buttons': button_data,
            'axes': axis_data,
            'hats': hat_data
        }
    
    def print_status(self, data: Dict):
        """打印当前状态"""
        if data is None:
            return False
            
        # 清屏
        print("\033[2J\033[H")  # 清屏并移动光标到顶部
        
        print("=" * 60)
        print("Xbox Controller Real-time Input Reader")
        print("Author: Yinchuan Li | Date: 2024-12-19")
        print("=" * 60)
        print("按 Ctrl+C 退出程序")
        print("-" * 60)
        
        # 显示按钮状态
        print("🔘 按钮状态 (Buttons):")
        print("-" * 30)
        active_buttons = []
        for btn_id, btn_info in data['buttons'].items():
            if btn_info['pressed']:
                status = "🔴 按下"
                active_buttons.append(btn_info['name'])
            else:
                status = "⚪ 释放"
            print(f"{btn_info['name']:<20} {status}")
        

        
        # 显示摇杆状态
        print("🎮 摇杆状态 (Axes):")
        print("-" * 30)
        for axis_id, axis_info in data['axes'].items():
            value = axis_info['value']
            # 创建可视化条
            bar_length = 20
            normalized_value = (value + 1) / 2  # 将-1到1映射到0到1
            filled_length = int(bar_length * normalized_value)
            bar = "█" * filled_length + "░" * (bar_length - filled_length)
            
            print(f"{axis_info['name']:<15} {value:6.3f} [{bar}]")
        print()
        
        # 显示帽子状态
        print("🎯 方向键状态 (D-Pad):")
        print("-" * 30)
        for hat_id, hat_info in data['hats'].items():
            if hat_info['value'] != (0, 0):
                print(f"{hat_info['name']}: {hat_info['direction']}")
            else:
                print(f"{hat_info['name']}: 中心")
        print()
        
        # 显示活动输入摘要
        print("📊 活动输入摘要:")
        print("-" * 30)
        active_inputs = []
        
        # 检查活动的摇杆
        for axis_id, axis_info in data['axes'].items():
            if abs(axis_info['value']) > 0.1:  # 阈值
                active_inputs.append(f"{axis_info['name']}: {axis_info['value']:.3f}")
        
        # 检查活动的帽子
        for hat_id, hat_info in data['hats'].items():
            if hat_info['value'] != (0, 0):
                active_inputs.append(f"{hat_info['direction']}")
        
        if active_buttons:
            active_inputs.extend(active_buttons)
            
        if active_inputs:
            print("活动输入:", ", ".join(active_inputs))
        else:
            print("无活动输入")
        
        print("=" * 60)
        return True
    
    def run(self, update_rate: float = 30.0):
        """运行主循环"""
        print("启动Xbox手柄读取器...")
        print("按 Ctrl+C 退出程序")
        time.sleep(1)
        
        try:
            while True:
                data = self.read_inputs()
                if not self.print_status(data):
                    break
                time.sleep(1.0 / update_rate)
                
        except KeyboardInterrupt:
            print("\n程序已退出")
        finally:
            pygame.quit()
    
    def get_button_state(self, button_id: int) -> bool:
        """获取特定按钮状态"""
        return self.button_states.get(button_id, False)
    
    def get_axis_state(self, axis_id: int) -> float:
        """获取特定轴状态"""
        return self.axis_states.get(axis_id, 0.0)
    
    def get_hat_state(self, hat_id: int) -> Tuple[int, int]:
        """获取特定帽子状态"""
        return self.hat_states.get(hat_id, (0, 0))


def main():
    """主函数"""
    try:
        # 创建Xbox手柄读取器
        controller = XboxControllerReader()
        
        # 运行主循环
        controller.run()
        
    except Exception as e:
        print(f"错误: {e}")
        print("请确保:")
        print("1. Xbox手柄已正确连接到电脑")
        print("2. 已安装pygame库 (pip install pygame)")
        print("3. 系统已识别手柄设备")


if __name__ == "__main__":
    main()
